package com.zcsy.commons.util;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.labels.CategoryToolTipGenerator;
import org.jfree.chart.labels.ItemLabelAnchor;
import org.jfree.chart.labels.ItemLabelPosition;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
import org.jfree.chart.labels.StandardPieToolTipGenerator;
import org.jfree.chart.labels.StandardXYItemLabelGenerator;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PiePlot;
import org.jfree.chart.plot.PiePlot3D;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.LineAndShapeRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.servlet.ServletUtilities;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.ui.TextAnchor;
import org.jfree.util.Rotation;
import org.springframework.cglib.core.CollectionUtils;

/**
 * 
 * @Title: JFreeChartUtil.java
 * @Package： com.zcsy.commons.util 
 * @Description: jfreechart生成报表工具类
 * @author chensf 735569870@qq.com
 * @Copyright: 2015 武汉中财数元信息技术有限公司 All rights reserved.
 * @date 2016年1月12日
 * @version 1.0
 * @see JDK1.7.0_75
 * @since
 */
public class JFreeChartUtil {
    
    /**
     * 
     * @Description <br>
     * 调柱状图表样式方法
     * @param chart JFreeChart对象
     * @param showValue 是否显示值
     * @return  JFreeChart对象
     * @author chensf 735569870@qq.com
     * @date 2016年1月13日
     * @see
     */
    private static JFreeChart setBarChartStyle(JFreeChart chart,boolean showValue){
        /**
         * VALUE_TEXT_ANTIALIAS_OFF表示将文字的抗锯齿关闭,
         * 使用关闭抗锯齿后，字体尽量选择12到14号的宋体字,这样文字最清晰好看
         */
        chart.getRenderingHints().put(RenderingHints.KEY_TEXT_ANTIALIASING,
                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        
        // 背景色
//        chart.setBackgroundPaint(Color.WHITE);
        
        // 设置标题字体
//        chart.getTitle().setFont(new Font("宋体", Font.BOLD, 14));
        
        // 图例背景色
//        chart.getLegend().setBackgroundPaint(new Color(110, 182, 229));
        
        // 图例字体
//        chart.getLegend().setItemFont(new Font("宋体", Font.PLAIN, 12));
     
        CategoryPlot categoryPlot = (CategoryPlot) chart.getPlot();
        
        // 设置纵虚线可见
        //categoryPlot.setDomainGridlinesVisible(true);
        
        // 虚线色彩
        //categoryPlot.setDomainGridlinePaint(Color.black);
        
        // 设置横虚线可见
        categoryPlot.setRangeGridlinesVisible(true);
        
        // 虚线色彩
        categoryPlot.setRangeGridlinePaint(Color.black);
        
        categoryPlot.setNoDataMessage("暂无数据");
        
        // 设置柱的透明度
        categoryPlot.setForegroundAlpha(1.0f);
        
        //设置柱图背景色（注意，系统取色的时候要使用
        //16位的模式来查看颜色编码，这样比较准确）
//        categoryPlot.setBackgroundPaint(new Color(110, 182, 229));
     
     
        // 纵坐标--范围轴
        NumberAxis numberAxis = (NumberAxis) categoryPlot.getRangeAxis();
        
        // 纵坐标y轴坐标字体
//        numberAxis.setTickLabelFont(new Font("sans-serif", Font.PLAIN, 12));
        
        // 纵坐标y轴标题字体
//        numberAxis.setLabelFont(new Font("宋体", Font.PLAIN, 12));
        
        // 设置最高的一个 Item 与图片顶端的距离
        // numberAxis.setUpperMargin(0.5);
        
        // 设置最低的一个 Item 与图片底端的距离
        // numberAxis.setLowerMargin(0.5);
        
        // 设置刻度单位 为Integer
        numberAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
     
        // 横坐标--类别轴、域
        CategoryAxis categoryAxis = categoryPlot.getDomainAxis();
        
        // 横坐标x轴坐标字体
//        categoryAxis.setTickLabelFont(new Font("sans-serif", Font.PLAIN, 12));
        
        // 横坐标x轴标题字体
//        categoryAxis.setLabelFont(new Font("宋体", Font.PLAIN, 12));
        
        // 类别轴的位置，倾斜度
//        categoryAxis.setCategoryLabelPositions(
//                CategoryLabelPositions.createUpRotationLabelPositions(
//                        0.5235987755982988D));
        //横轴上的 Lable
        //categoryAxis.setMaximumCategoryLabelWidthRatio(0.6f);

        //设置距离图片左端距离
//        categoryAxis.setLowerMargin(0.1D);
        
        // 设置距离图片右端距离
//        categoryAxis.setUpperMargin(0.1D);
     
        // 渲染 - 中间的部分
        BarRenderer barRenderer = (BarRenderer) categoryPlot.getRenderer();
        
        // 设置柱子最大宽度
        barRenderer.setMaximumBarWidth(0.05);
        
        // 设置柱子最小高度
        barRenderer.setMinimumBarLength(0.2);
        
        // 设置柱子边框颜色
        barRenderer.setBaseOutlinePaint(Color.BLACK);
        
        // 设置柱子边框可见
        barRenderer.setDrawBarOutline(true);
        
        // 设置每个柱之间距离
        barRenderer.setItemMargin(0.2D);
        
        // 显示每个柱的数值，并修改该数值的字体属性
        if(showValue){
            
            barRenderer.setBaseItemLabelGenerator(
                new StandardCategoryItemLabelGenerator());
            barRenderer.setBaseItemLabelsVisible(true);
        }
     
        return chart;
    }
    
    /**
     * 
     * @Description <br>
     * 生成柱状图报表方法
     * @param dataList 将要生成报表的数据
     * dataList为ArrayList<ArrayList<String>>类型，其格式为
     *            arrayList---------------------- |----ArrayLisy<String> 行数据 |----ArrayLisy<String>
     *            行数据 |----ArrayLisy<String> 行数据 |........ArrayList<String>里放3个值，第一个为数量，第二个为一级分类，第三个为二级分类
     * @param title 报表标题
     * @param xTitle x轴标题
     * @param yTitle y轴标题
     * @param isVertical 报表是否垂直显示
     * @param useLegend 是否使用legend
     * @param useTooltip 是否使用tooltip
     * @param useUrl 是否使用url链接 
     * @param width 图片宽
     * @param height 图片高
     * @param is3D 是否3D显示
     * @param showValue 是否显示值
     * @param session 会话
     * @param request 请求
     * @return 返回生成的图片名和图片路径的map集合
     * @author chensf 735569870@qq.com
     * @date 2016年1月12日
     * @see
     */
    public static Map<String, String> createBarReport(ArrayList<ArrayList<String>> dataList,String title,String xTitle,String yTitle,
                                      boolean isVertical,boolean useLegend,boolean useTooltip,boolean useUrl,int width,int height,
                                      boolean is3D,boolean showValue,HttpSession session,HttpServletRequest request){
        
        String graphURL = "";
        String filename = "";
        Map<String, String> map = new HashMap<String, String>();
        
        try {
            
            //获取dataset
            DefaultCategoryDataset dataset = new DefaultCategoryDataset();
            
            //将数据放入到dataset中
            if(dataList.size() > 0){
                for(List<String> item : dataList){
                    dataset.addValue(Float.valueOf(item.get(0)), item.get(1), item.get(2));
                }
            }
            
            //判断柱状图是垂直方向还是水平方向
            PlotOrientation direction = null;
            if(isVertical){
                
                direction = PlotOrientation.VERTICAL;
            }else{
                
                direction = PlotOrientation.HORIZONTAL;
            }
            
            JFreeChart chart = null;
            if(is3D){
                
                chart = ChartFactory.createBarChart3D(title, xTitle, yTitle, dataset,
                    direction, useLegend,useTooltip,useUrl);
            }else{
                
                chart = ChartFactory.createBarChart(title, xTitle, yTitle, dataset,
                    direction, useLegend,useTooltip,useUrl);
            }
            
            setBarChartStyle(chart,showValue);
            
            //生成报表图片并返回图片名称
            filename = ServletUtilities.saveChartAsPNG(chart, width, height, null, session);
            
            //获取图片路径
            graphURL = request.getContextPath() + "/DisplayChart?filename=" + filename;
            
            map.put("filename", filename);
            map.put("graphURL", graphURL);
            
        } catch (IOException e) {
            
            e.printStackTrace();
        }
        
        
        return map;
        
    }
    
    /**
     * 
     * @Description <br>
     * 调整饼状图样式方法
     * @param chart JFreeChart对象
     * @param set3Dstyle 是否为透明的3D样式
     * @param isCircular 是否圆形
     * @param showPercent 是否显示百分比
     * @return JFreeChart对象
     * @author chensf 735569870@qq.com
     * @date 2016年1月13日
     * @see
     */
    private static JFreeChart setPieChartStyle(JFreeChart chart,boolean set3Dstyle,boolean isCircular,boolean showPercent){
        /**
         * VALUE_TEXT_ANTIALIAS_OFF表示将文字的抗锯齿关闭,
         * 使用关闭抗锯齿后，字体尽量选择12到14号的宋体字,这样文字最清晰好看
         */
        chart.getRenderingHints().put(RenderingHints.KEY_TEXT_ANTIALIASING,
                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        
        // 背景色
//        chart.setBackgroundPaint(Color.WHITE);
        
        // 设置标题字体
//        chart.getTitle().setFont(new Font("宋体", Font.BOLD, 14));
        
        // 图例背景色
//        chart.getLegend().setBackgroundPaint(new Color(110, 182, 229));
        
        // 图例字体
//        chart.getLegend().setItemFont(new Font("宋体", Font.PLAIN, 12));
        
     // 设置图例说明Legend上的文字  
        chart.getLegend().setItemFont(new Font("宋体", Font.PLAIN, 14)); 
        
        PiePlot plot = (PiePlot) chart.getPlot();;
        if(set3Dstyle){
            
          //设置透明度，0.5F为半透明，1为不透明，0为全透明  
          plot.setForegroundAlpha(0.5F);
            
        }
        
        // 设置第一个 饼块section 的开始位置，默认是12点钟方向  
        plot.setStartAngle(0D);    
         
         //设置方向为”顺时针方向“  
        plot.setDirection(Rotation.ANTICLOCKWISE); 
         
         //设置没有数据时显示的内容
        plot.setNoDataMessage("暂无数据");
         
        // 图形边框颜色  
        plot.setBaseSectionOutlinePaint(Color.RED);  
        
        // 图形边框粗细  
        plot.setBaseSectionOutlineStroke(new BasicStroke(1.0f));  

        // 指定显示的饼图上圆形(true)还椭圆形(false)  
        plot.setCircular(isCircular);  

        // 设置鼠标悬停提示  
        plot.setToolTipGenerator(new StandardPieToolTipGenerator());  

        // 设置突出显示的数据块  
        plot.setExplodePercent("One", 0.1D);  
        // 设置饼图各部分标签字体  
        plot.setLabelFont(new Font("宋体", Font.ITALIC, 14));  

        if(showPercent){
            
           //显示每部分的内容加每部分的值及百分比({0}表示每部分的名称，{1}表示每部分的值，{2}表示每部分的百分比)
//            plot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0}={1}({2})"));
            
           //显示每部分的内容+百分比(百分比没有小数)
//           plot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0}({2})"));
           //显示每部分的内容+百分比(百分比一位小数)
           plot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0}({2})",new DecimalFormat("0.0"),new DecimalFormat("0.0%")));
        }
        
        return chart;
    }
    
    /**
     * 
     * @Description <br>
     * 生成饼状图方法
     * @param dataList 数据源
     * dataList为ArrayList<ArrayList<String>>，其格式为
     *            arrayList---------------------- |----ArrayLisy<String> 行数据 |----ArrayLisy<String>
     *            行数据 |----ArrayLisy<String> 行数据 |........ArrayLisy<String>里放两个值，第一个为每部分名称，第二个为每部分的值
     * @param title 标题
     * @param set3Dstyle 是否为3D透明的
     * @param isCircular 是否是圆形
     * @param useLegend 是否使用legend
     * @param useTooltip 是否使用tooltip
     * @param useUrl 是否使用url
     * @param width 图片宽
     * @param height 图片高
     * @param is3D 是否是3D
     * @param showPercent 是否显示百分比
     * @param session 会话
     * @param request 请求
     * @return 返回生成的图片名和图片路径的map集合
     * @author chensf 735569870@qq.com
     * @date 2016年1月13日
     * @see
     */
    public static Map<String, String> createPieChart(ArrayList<ArrayList<String>> dataList,String title,boolean set3Dstyle,boolean isCircular,
                                                      boolean useLegend,boolean useTooltip,boolean useUrl,int width,int height,boolean is3D,
                                                      boolean showPercent,HttpSession session,HttpServletRequest request){
        
        String graphURL = "";
        String filename = "";
        Map<String, String> map = new HashMap<String, String>();
        
        try {
            
            //获取dataset
            DefaultPieDataset dataset = new DefaultPieDataset();
            
            //将数据放入到dataset中
            if(dataList.size() > 0){
                for(List<String> item : dataList){
                    dataset.setValue(item.get(0),Float.valueOf(item.get(1)));
                }
            }
            
            JFreeChart chart = null;
            if(is3D){
                
                chart = ChartFactory.createPieChart3D(title, dataset,useLegend,useTooltip,useUrl);
            }else{
                
                chart = ChartFactory.createPieChart(title, dataset, useLegend,useTooltip,useUrl);
            }
            
            setPieChartStyle(chart,set3Dstyle,isCircular,showPercent);
            
            //生成报表图片并返回图片名称
            filename = ServletUtilities.saveChartAsPNG(chart, width, height, null, session);
            
            //获取图片路径
            graphURL = request.getContextPath() + "/DisplayChart?filename=" + filename;
            
            map.put("filename", filename);
            map.put("graphURL", graphURL);
            
        } catch (IOException e) {
            
            e.printStackTrace();
        }
        
        return map;
    }
    
    /**
     * 
     * @Description <br>
     * 调整折线图样式方法
     * @param chart JFreeChart对象
     * @param showValue 是否显示数值
     * @return JFreeChart对象
     * @author chensf 735569870@qq.com
     * @date 2016年1月14日
     * @see
     */
    private static JFreeChart setLineChartStyle(JFreeChart chart,boolean showValue){
        /**
         * VALUE_TEXT_ANTIALIAS_OFF表示将文字的抗锯齿关闭,
         * 使用关闭抗锯齿后，字体尽量选择12到14号的宋体字,这样文字最清晰好看
         */
        chart.getRenderingHints().put(RenderingHints.KEY_TEXT_ANTIALIASING,
                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        
        // 背景色
//        chart.setBackgroundPaint(Color.WHITE);
        
        // 设置标题字体
//        chart.getTitle().setFont(new Font("宋体", Font.BOLD, 14));
        
        // 图例背景色
//        chart.getLegend().setBackgroundPaint(new Color(110, 182, 229));
        
        // 图例字体
//        chart.getLegend().setItemFont(new Font("宋体", Font.PLAIN, 12));
     
        CategoryPlot plot = (CategoryPlot)chart.getPlot();
        
        // 图像属性部分
        plot.setBackgroundPaint(Color.white);
        
        //设置横向背景网格线是否可见
        plot.setDomainGridlinesVisible(true);  
        
        //设置横向背景网格线颜色
        plot.setDomainGridlinePaint(Color.BLACK); 
        
        //设置纵向背景网格线是否可见
        plot.setRangeGridlinesVisible(true);  
        //设置纵向背景网格线颜色
        plot.setRangeGridlinePaint(Color.GRAY);
        
        //没有数据时显示的文字说明
        plot.setNoDataMessage("暂无数据");
        
        // 边框粗细  
        plot.setOutlineStroke(new BasicStroke(1.5f)); 
        
        //获取横坐标
        CategoryAxis vaxis = plot.getDomainAxis();  
        
        // 坐标轴粗细 
        vaxis.setAxisLineStroke(new BasicStroke(1.5f));  
        
        // 坐标轴颜色
        vaxis.setAxisLinePaint(new Color(215, 215, 215));   
        
        // 坐标轴标题颜色  
        vaxis.setLabelPaint(new Color(10, 10, 10)); 
        
        // 坐标轴标尺值颜色  
        vaxis.setTickLabelPaint(new Color(102, 102, 102)); 
        
        // 分类轴下（左）边距 
        vaxis.setLowerMargin(0.06d); 
        
        // 分类轴下（右）边距,防止最后边的一个数据靠近了坐标轴。  
        vaxis.setUpperMargin(0.14d);
          
        ValueAxis valueAxis = plot.getRangeAxis();  
 
        valueAxis.setAxisLineStroke(new BasicStroke(1.5f)); // 坐标轴粗细  
        valueAxis.setAxisLinePaint(new Color(215, 215, 215)); // 坐标轴颜色  
        valueAxis.setLabelPaint(new Color(10, 10, 10)); // 坐标轴标题颜色  
        valueAxis.setTickLabelPaint(new Color(102, 102, 102)); // 坐标轴标尺值颜色  
          
 
        LineAndShapeRenderer lineRenderer = (LineAndShapeRenderer) plot  
                .getRenderer();  
        
        //设置数据节点是否可见
        lineRenderer.setBaseShapesVisible(true);
        
        // 显示节点的值  
        if(showValue){
            
            lineRenderer.setBaseItemLabelsVisible(true);  
            lineRenderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator());
        }
     
        return chart;
    }
    
    /**
     * 
     * @Description <br>
     * 生成折线图方法
     * @param dataList 数据源，其格式为
     *            arrayList---------------------- |----ArrayLisy<String> 行数据 |----ArrayLisy<String>
     *            行数据 |----ArrayLisy<String> 行数据 |........ArrayList<String>里放3个值，第一个为数量，第二个为一级分类，第三个为二级分类
     * @param title 标题
     * @param xTitle x轴标题
     * @param yTitle y轴标题
     * @param useLegend 是否使用legend
     * @param useTooltip 是否使用tooltip
     * @param useUrl 是否使用url
     * @param width 宽
     * @param height 高
     * @param is3D 是否是3D
     * @param showValue 是否显示数值
     * @param session 会话
     * @param request 请求
     * @return 返回生成的图片名和图片路径的map集合
     * @author chensf 735569870@qq.com
     * @date 2016年1月14日
     * @see
     */
    public static Map<String, String> createLineReport(ArrayList<ArrayList<String>> dataList,String title,String xTitle,String yTitle,
                                                      boolean useLegend,boolean useTooltip,boolean useUrl,int width,int height,boolean is3D,
                                                      boolean showValue,HttpSession session,HttpServletRequest request){
                        
                        String graphURL = "";
                        String filename = "";
                        Map<String, String> map = new HashMap<String, String>();
                        
                        try {
                            
                            //获取dataset
                            DefaultCategoryDataset dataset = new DefaultCategoryDataset();
                            
                            //将数据放入到dataset中
                            if(dataList.size() > 0){
                                for(List<String> item : dataList){
                                    dataset.addValue(Float.valueOf(item.get(0)), item.get(1), item.get(2));
                                }
                            }
                            
                            JFreeChart chart = null;
                            if(is3D){
                                
                                chart = ChartFactory.createLineChart3D(title, xTitle, yTitle, dataset,
                                    PlotOrientation.VERTICAL, useLegend,useTooltip,useUrl);
                            }else{
                                
                                chart = ChartFactory.createLineChart(title, xTitle, yTitle, dataset,
                                    PlotOrientation.VERTICAL, useLegend,useTooltip,useUrl);
                            }
                            
                            setLineChartStyle(chart,showValue);
                            
                            //生成报表图片并返回图片名称
                            filename = ServletUtilities.saveChartAsPNG(chart, width, height, null, session);
                            
                            //获取图片路径
                            graphURL = request.getContextPath() + "/DisplayChart?filename=" + filename;
                            
                            map.put("filename", filename);
                            map.put("graphURL", graphURL);
                            
                        } catch (IOException e) {
                            
                            e.printStackTrace();
                        }
                        
                        return map;
                        
                    }
}
